package tx

import (
	"fmt"
	"longmen/server/config/global"
	"longmen/server/pkg/common/helper/http"
	string2 "longmen/server/pkg/common/helper/string"
	"longmen/server/pkg/common/helper/util"
	"longmen/server/pkg/db/redis/cache"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
)

type QcloudSTS struct {
}
type Policy struct {
	Version   string      `json:"version"`
	Statement []Statement `json:"statement"`
}
type Statement struct {
	Action    []string  `json:"action"`
	Effect    string    `json:"effect"`
	Principal Principal `json:"principal"`
	Resource  []string  `json:"resource"`
}
type Principal struct {
	Qcs []string `json:"qcs"`
}

// 获取临时密钥
func (s *QcloudSTS) GetTempKeys(config map[string]interface{}) (map[string]interface{}, error) {
	fName := "GetTempKeys"
	var policy interface{}
	if _, ok := config["policy"]; ok {
		policy = config["policy"]
	} else {
		appId := ""
		if _, ok := config["bucket"]; !ok {
			return nil, nil
		}
		bucket, ok := config["bucket"].(string)
		if !ok {
			return nil, fmt.Errorf("%s bucket not a string", util.Convert(fName))
		}
		buckets := strings.Split(bucket, "-")
		if len(buckets) < 2 {
			return nil, fmt.Errorf("%s buckets len lt 2", util.Convert(fName))
		}
		appId = buckets[1]

		if _, ok := config["allowPrefix"]; !ok {
			return nil, nil
		}
		allowPrefix, ok := config["allowPrefix"].(string)
		if !ok {
			return nil, fmt.Errorf("%s allowPrefix not a string", util.Convert(fName))
		}
		if allowPrefix[0:1] != "/" {
			allowPrefix = "/" + allowPrefix
		}

		allowActions, ok := config["allowActions"].([]string)
		if !ok {
			return nil, fmt.Errorf("%s allowActions not a string slice", util.Convert(fName))
		}
		region, ok := config["region"].(string)
		if !ok {
			return nil, fmt.Errorf("%s region not a string", util.Convert(fName))
		}

		policy = Policy{
			Version: "2.0",
			Statement: []Statement{{
				Action:    allowActions,
				Effect:    "allow",
				Principal: Principal{Qcs: []string{"*"}},
				Resource:  []string{"qcs::cos:" + region + ":uid/" + appId + ":" + bucket + allowPrefix},
			}},
		}
	}

	policyStr, err := util.Struct2Json(policy)
	if err != nil {
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}
	Action := "GetFederationToken"
	Nonce := util.I2S(string2.RandInt(10000, 20000))
	Timestamp := strconv.FormatInt(time.Now().Unix(), 10)
	Method := "POST"
	if _, ok := config["durationSeconds"]; ok {
		if _, ok := config["durationSeconds"].(int); !ok {
			return nil, nil
		}
	}
	params := map[string]string{
		"SecretId":        config["secretId"].(string),
		"Timestamp":       Timestamp,
		"Nonce":           Nonce,
		"Action":          Action,
		"DurationSeconds": util.I2S(config["durationSeconds"].(int)),
		"Version":         "2018-08-13",
		"Name":            "cos",
		"Region":          config["region"].(string),
		"Policy":          url.QueryEscape(policyStr),
	}
	params["Signature"] = getSignature(params, config["secretKey"].(string), Method, config)
	urlPath := config["url"].(string)
	paramsStr := json2str(params, false)
	//todo proxy
	resp, err := http.HttpPost(urlPath, paramsStr, 5000, 10000, "")
	if err != nil {
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}
	result := make(map[string]interface{})
	err = util.Json2Struct(string(resp), &result)
	if err != nil {
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}
	if _, ok := result["Response"]; ok {
		result, _ = result["Response"].(map[string]interface{})
		if _, ok := result["Error"]; ok {
			return nil, fmt.Errorf("%s %v", util.Convert(fName), result["Error"])
		}
	}
	result["startTime"] = int(result["ExpiredTime"].(float64)) - config["durationSeconds"].(int)
	return result, nil
}

// obj 转 query string
func json2str(obj map[string]string, notEncode bool) string {
	//strings.Replace(url.QueryEsgetSignaturecape(str), "+", "%20", -1)
	result := make([]string, 0)
	var keys []string
	for k := range obj {
		keys = append(keys, k)
	}
	sort.Slice(keys, func(i, j int) bool { return strings.ToLower(keys[i]) < strings.ToLower(keys[j]) })
	for _, k := range keys {
		if notEncode {
			result = append(result, fmt.Sprintf("%s=%s", k, obj[k]))
			continue
		}
		result = append(result, fmt.Sprintf("%s=%s", k, strings.Replace(url.QueryEscape(obj[k]), "+", "%20", -1)))
	}
	return strings.Join(result, "&")
}

// Action、SecretId、Timestamp、Nonce和Region
func (this *QcloudSTS) GetOnLiveLive(streamId string) (bool, error) {
	action := "DescribeLiveStreamOnlineList"
	configPri := cache.GetConfigPri()
	Timestamp := strconv.FormatInt(time.Now().Unix(), 10)
	Nonce := util.I2S(string2.RandInt(10000, 20000))
	inputParams := make(map[string]string)
	inputParams["SecretId"] = configPri.QcloudSecretid
	inputParams["Timestamp"] = Timestamp
	inputParams["Nonce"] = Nonce
	inputParams["Region"] = configPri.CosRegion
	inputParams["Action"] = action
	inputParams["Version"] = "2018-08-01"
	inputParams["StreamName"] = streamId

	config := make(map[string]interface{})
	config["domain"] = "live.tencentcloudapi.com"

	params := map[string]string{
		"SecretId":   configPri.QcloudSecretid,
		"Timestamp":  Timestamp,
		"Nonce":      Nonce,
		"Action":     action,
		"Region":     configPri.CosRegion,
		"Version":    "2018-08-01",
		"StreamName": streamId,
	}

	inputParams["Signature"] = getSignature(params, configPri.QcloudSecretkey, "GET", config)
	paramsStr := json2str(inputParams, false)
	resp, err := http.HttpGet("https://"+config["domain"].(string)+"/?"+paramsStr, 5000, 10000, nil)

	if err != nil {
		global.Log.Errorf("流检测错误:%v", string(resp))
		return false, err
	}

	var list *DescribeLiveStreamOnlineListResp
	err = util.Json2Struct(string(resp), &list)

	if err != nil {
		return false, err
	}

	if list.Response.TotalNum > 0 {
		for _, c := range list.Response.OnlineInfo {
			if c.StreamName == streamId {
				return true, nil
			}
		}
		return false, nil

	}
	return false, nil
}

// 计算临时密钥用的签名
func getSignature(opt map[string]string, key string, method string, config map[string]interface{}) string {
	domain, _ := config["domain"].(string)
	formatString := method + domain + "/?" + json2str(opt, true)
	// fmt.Println(formatString)
	return common.Sign(formatString, key, common.SHA1)
}

type DescribeLiveStreamOnlineListResp struct {
	Response *DescribeLiveStreamOnlineList `json:"Response"`
}
type DescribeLiveStreamOnlineList struct {
	TotalNum   int           `json:"TotalNum"` //符合条件的总个数。
	TotalPage  int           `json:"TotalPage"`
	PageNum    int           `json:"PageNum"`
	PageSize   int           `json:"PageSize"`
	RequestId  string        `json:"RequestId"`  //唯一请求 ID，每次请求都会返回。定位问题时需要提供该次请求的 RequestId。
	OnlineInfo []*OnlineInfo `json:"OnlineInfo"` //正在推送流的信息列表。
}
type OnlineInfo struct {
	StreamName      string             `json:"StreamName"`      //流名称。
	PublishTimeList []*PublishTimeList `json:"PublishTimeList"` //推流时间列表
	DomainName      string             `json:"DomainName"`      //推流域名。
	AppName         string             `json:"AppName"`         //	应用名称。
}
type PublishTimeList struct {
	PublishTime string `json:"PublishTime"` //推流时间
}
